#pragma once

#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <mutex>
#include <queue>

int DefaultCap = 5;

// 1.场景: a.队满->通知消费者来消费  b.队空->通知生产者来生产
// 2.加锁: consumer在pop的时候, prod在push
// 3.条件变量: 判断临界资源是否就绪

template <class T>
class BlockQueue
{
public:
    bool isEmptyQueue() { return _bq.size() == 0; }
    bool isFUllQueue() { return _bq.size() == _capacity; }
    
    BlockQueue(int capacity = DefaultCap)
        : _capacity(capacity)
    {
        pthread_mutex_init(&_mtx, nullptr);
        pthread_cond_init(&_Empty, nullptr);
        pthread_cond_init(&_Full, nullptr);
    }

    void Push(const T &in)
    {
        pthread_mutex_lock(&_mtx);

        // 1.检测临界资源是否就绪
        // pthread_cond_wait成功调用后,传入的锁会被自动释放
        // 当被唤醒的时候, pthread_cond_wait, 会自动帮线程获取锁

        //pthread_cond_wait是一个函数, 只要是函数,就可能会调用失败
        //pthread_cond_wait存在伪唤醒情况(可能条件不满足就唤醒了, 需要再次检测-->while)
        while(isFUllQueue()) pthread_cond_wait(&_Full, &_mtx);
        // 2.访问临界资源
        _bq.push(in);
        pthread_cond_signal(&_Empty); // 生产者唤醒消费者
        pthread_mutex_unlock(&_mtx);
    }

    void Pop(T *out)
    {
        pthread_mutex_lock(&_mtx);
        while(isEmptyQueue())pthread_cond_wait(&_Empty, &_mtx);
        *out = _bq.front();
        _bq.pop();
        pthread_cond_signal(&_Full); // 消费者唤醒生产者
        pthread_mutex_unlock(&_mtx);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_Empty);
        pthread_cond_destroy(&_Full);
    }

private:
    std::queue<T> _bq;     // 阻塞队列
    int _capacity;         // 容量上限
    pthread_mutex_t _mtx;  // 锁->安全
    pthread_cond_t _Empty; // 队列空否? --> productor
    pthread_cond_t _Full;  // 队列满否? --> consumer
};
